﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows.Input;
using DependencyAnalysis;
using DependencyAnalysis.DataModel;
using DependencyAnalysis.Internal;
using theeg.DependencyAnalysis.CommonAddin.Behaviors;

namespace theeg.DependencyAnalysis.CommonAddin.Models
{
    public class CommonTestsModel : ViewModelBase
    {
        private readonly IAssert _assert = new RuleAssert();

        private readonly ICommand _runSelectedCommand;

        public ICommand RunSelectedCommand
        {
            get { return _runSelectedCommand; }
        }

        private readonly ICommand _runAllCommand;

        public ICommand RunAllCommand
        {
            get { return _runAllCommand; }
        }

        private readonly ICommand _removeCommand;

        public ICommand RemoveCommand
        {
            get { return _removeCommand; }
        }

        private readonly ICommand _addCommand;

        public ICommand AddCommand
        {
            get { return _addCommand; }
        }

        private readonly IList<IRuleDescriptor> _rules = new ObservableCollection<IRuleDescriptor>();

        public IEnumerable<IRuleDescriptor> Rules
        {
            get { return _rules; }
        }

        private IRuleDescriptor _selectedRule;

        public IRuleDescriptor SelectedRule
        {
            get { return _selectedRule; }
            set
            {
                if (Equals(value, _selectedRule)) return;
                _selectedRule = value;
                RaisePropertyChanged("SelectedRule");
            }
        }

        private IVerifier _verifier;

        private Metrics _metrics;

        private IDependencyModel _model;

        public IDependencyModel Model
        {
            get { return _model; }
            set
            {
                if (Equals(value, _model)) return;
                _model = value;
                if (_model != null)
                {
                    _verifier = new Verifier(_model);
                    _metrics = new MetricsImplementation(_model);
                }
                RaisePropertyChanged("Model");
            }
        }

        public CommonTestsModel()
        {
            _runAllCommand = new RelayCommand(RunAllExecuted, RunAllCanExecute);
            _runSelectedCommand = new RelayCommand(RunSelectedExecuted, RunSelectedCanExecute);
            _addCommand = new RelayCommand(AddExecuted);
        }

        public void Load()
        {
            try
            {
                var path = Path.GetDirectoryName(GetType().Assembly.Location) + "\\CommonTests.serialized";
                var fileStream = new FileStream(path, FileMode.Open);
                var serializer = new DataContractSerializer(typeof(RuleDescriptor[]), null, int.MaxValue, true, true, null);
                var loadedRules = serializer.ReadObject(fileStream) as RuleDescriptor[];
                _rules.Clear();
                if (loadedRules == null)
                {
                    return;
                }
                foreach (var ruleDescriptor in loadedRules)
                {
                    _rules.Add(ruleDescriptor);
                }
            }
            catch (Exception)
            {
            }
        }

        private void AddExecuted(object obj)
        {
            _rules.Add(new RuleDescriptor());
        }

        private bool RunSelectedCanExecute(object obj)
        {
            return SelectedRule != null && Model != null;
        }

        private void RunSelectedExecuted(object obj)
        {
            RunRule(SelectedRule);
        }

        private void RunRule(IRuleDescriptor currentRule)
        {
            var compiledRule = currentRule.Compiled;
            try
            {
                if (currentRule.State == RunState.Uncompiled || compiledRule == null)
                {
                    compiledRule = RuleCompiler.Compile(currentRule);
                }
            }
            catch (Exception e)
            {
                currentRule.State = RunState.Exception;
                currentRule.Results = e.Message;
                return;
            }
            try
            {
                compiledRule.Model = Model;
                compiledRule.Metrics = _metrics;
                compiledRule.Verifier = _verifier;
                compiledRule.Assert = _assert;
                compiledRule.Execute();
            }
            catch (Exception e)
            {
                currentRule.State = RunState.Assert;
                currentRule.Results = e.Message;
                return;
            }
            currentRule.State = RunState.Success;
        }

        private bool RunAllCanExecute(object obj)
        {
            return Model != null && Rules != null;
        }

        private void RunAllExecuted(object obj)
        {
            var rules = Rules.ToArray();
            foreach (var rule in rules)
            {
                RunRule(rule);
            }
        }
    }
}
